﻿using System;
using System.Collections.Generic;
using System.Threading;
using System.Xml.Linq;
using System.Linq;
using System.Xml;
using System.Text.RegularExpressions;
using System.ComponentModel;
using System.IO;
using MediaPortal.GUI.Library;
using System.Runtime.Remoting.Messaging;
using MediaPortal.Configuration;

namespace MyNetflix2
{
    public class QueueItem : ImageProcessor
    {
        #region Locals

        private XElement Source;
        private List<string> _genres = new List<string>();
        private List<string> _ratings = new List<string>();
        private List<Formats> _titleFormats = new List<Formats>();
        private List<string> _boxArt = new List<string>();
        public List<Thread> ImageThreads = new List<Thread>();
        private bool _isSeries = false;
        private bool _isEpisode = false;
        private bool _isInstant = false;
        private bool _isDvd = false;
        private bool _isBluRay = false;
        private bool _hasEpisodes = false;
        private int _runtime;
        private string _altTitle = "";
        private string _titleRef;
        private string _similarLink;
        private string _availability;
        private string _synopsisLink;
        private string _episodesLink;
        private string _seasonsLink;
        private string _castLink;
        private string _discLink;
        private string _formatsLink;
        private string _coverId;
        private string _backdropId;
        private string _synopsis;
        private string _theMovieDbKey = "";
        private string _theTVDbKey = "";
        private string _theTVDbId = "";
        private User _user = null;

        #endregion

        #region Public

        public int Position
        {
            get { return int.Parse(Source.Element("position").Value); }
        }
        public string ID
        {
            get { return Source.Element("id").Value; }
        }
        public string AverageRating
        {
            get { return Source.Element("average_rating") == null ? "0.0" : Source.Element("average_rating").Value; }
        }
        public string Availability
        {
            get { return _availability; }
        }
        public string AltTitle
        {
            get { return _altTitle; }
            set { _altTitle = value; }
        }
        public string Title
        {
            get{return Source.Element("title").Attribute("regular").Value;}
        }
        public string ShortTitle
        {
            get { return Source.Element("title").Attribute("episode_short_raw") == null ? "" : Source.Element("title").Attribute("episode_short_raw").Value; }
        }
        public DateTime Updated
        {
            get { return ClrExtensions.DateTimeExtension.FromUnixTime(long.Parse(Source.Element("updated").Value)); }
        }
        public int ReleaseYear
        {
            get { return int.Parse(Source.Element("release_year").Value); }
        }
        public int RunTime
        {
            get {return  _runtime; }
            set { _runtime = value; }
        }
        public List<string> Genres
        {
            get { return _genres; }
        }
        public List<string> Ratings
        {
            get { return _ratings; }
        }
        public bool IsSeries
        {
            get { return _isSeries; }
        }
        public bool IsEpisode
        {
            get { return _isEpisode; }
        }
        public bool HasEpisodes
        {
            get { return _hasEpisodes; }
        }
        public bool IsInstant
        {
            get { return _isInstant; }
        }
        public bool IsDvd
        {
            get { return _isDvd; }
        }
        public bool IsBluRay
        {
            get { return _isBluRay; }
        }
        public string TitleRef
        {
            get { return _titleRef; }
        }
        public List<Formats> TitleFormats
        {
            get { return _titleFormats; }
        }
        public string SimilarLink
        {
            get { return _similarLink; }
        }
        public List<string> BoxArt
        {
            get { return _boxArt; }
        }
        public string SynopsisLink
        {
            get { return _synopsisLink; }
        }
        public string SeasonsLink
        {
            get { return _seasonsLink; }
        }
        public string EpisodesLink
        {
            get { return _episodesLink; }
        }
        public string CastLink
        {
            get { return _castLink; }
        }
        public string DiscLink
        {
            get { return _discLink; }
        }
        public string FormatsLink
        {
            get { return _formatsLink; }
        }
        public string CoverId
        {
            get { return _coverId; }
        }
        public string BackDropId
        {
            get { return _backdropId; }
        }
        public string Synopsis
        {
            get { return _synopsis; }
        }
        public string TheTVDbId
        {
            get { return _theTVDbId; }
        }
        #endregion

        #region Constructors

        public QueueItem()
        { }

        /// <summary>
        /// Constructor for Queues
        /// </summary>
        /// <param name="source"></param>
        public QueueItem(XElement source, User user)
        {
            //--------------------------------------------------------------------------------
            // Lets save the original source for debugging
            //--------------------------------------------------------------------------------
            Source = source;
            _user = user;
            
            if (ShortTitle != "")
                _isEpisode = true;
            //--------------------------------------------------------------------------------
            // Linq wont parse the Categories, Links or Box Art, so lets get them seperately
            //--------------------------------------------------------------------------------
            GetCategories();
            GetLinks();
            GetBoxArt();
            GetMisc();

            //--------------------------------------------------------------------------------
            // Get the background images
            //--------------------------------------------------------------------------------
            #region Get Background image

            if (IsSeries)
            {
                Thread bannerImage = new Thread(GetSeriesBackgroundImage);
                bannerImage.Start();
                ImageThreads.Add(bannerImage);
            }
            else
            {
                //--------------------------------------------------------------------------------
                // If the file exists exit.
                //--------------------------------------------------------------------------------
                if (!File.Exists(Config.GetFolder(Config.Dir.Thumbs) + "\\MyNetFlix\\BackDrops\\" + _backdropId + ".jpg"))
                {
                    Thread bannerImage = new Thread(GetBackgroundImage);
                    bannerImage.Start();
                    ImageThreads.Add(bannerImage);
                }
            }

            //--------------------------------------------------------------------------------
            // Get the cover image
            // If the file exists exit.
            //--------------------------------------------------------------------------------
            if (!File.Exists(Config.GetFolder(Config.Dir.Thumbs) + "\\MyNetFlix\\Covers\\" + _coverId + ".jpg"))
            {
                Thread coverImage = new Thread(GetCoverImage);
                coverImage.Start();
                ImageThreads.Add(coverImage);
            }

            #endregion

            if (_isSeries && _episodesLink != null)
                _hasEpisodes = true;

            Log.Debug("My NetFlix - QueueItem:Constructor(): Movie " + Title + " added to the queue");
        }

        /// <summary>
        /// Cosntructor for Episodes
        /// </summary>
        /// <param name="connection"></param>
        /// <param name="source"></param>
        public QueueItem(XElement source, User user, XElement episodeXML, string bannerImage)
        {
            //--------------------------------------------------------------------------------
            // Lets save the original source for debugging
            //--------------------------------------------------------------------------------
            Source = source;
            _user = user;
            ImageProcessor image = new ImageProcessor();

            //--------------------------------------------------------------------------------
            // Save the Synopsis from the xml
            //--------------------------------------------------------------------------------
            _isEpisode = true;

            //--------------------------------------------------------------------------------
            // Linq wont parse the Categories, Links or Box Art, so lets get them seperately
            //--------------------------------------------------------------------------------
            GetCategories();
            GetLinks();
            GetBoxArt();

            //--------------------------------------------------------------------------------
            // Lets get only get the banner, cover and episode info if its included...
            //--------------------------------------------------------------------------------
            if (episodeXML != null)
            {
                GetEpisodeInfo(episodeXML);

                //--------------------------------------------------------------------------------
                // Save the backdrop & cover images
                //--------------------------------------------------------------------------------
                image.asyncGetBanner(bannerImage, BackDropId);
                image.asyncGetCover("http://thetvdb.com/banners/" + episodeXML.Element("filename").Value, CoverId);
            }
            else
            {
                //--------------------------------------------------------------------------------
                // Get the cover image
                // If the file exists exit.
                //--------------------------------------------------------------------------------
                if (!File.Exists(Config.GetFolder(Config.Dir.Thumbs) + "\\MyNetFlix\\Covers\\" + _coverId + ".jpg"))
                {
                    Thread coverImage = new Thread(GetCoverImage);
                    coverImage.Start();
                    ImageThreads.Add(coverImage);
                }
            }

            Log.Debug("My NetFlix - QueueItem:Constructor(): Movie " + Title + " added to the queue");
        }

        #endregion

        #region 'Get' Methods

        /// <summary>
        /// Get episode specific information
        /// </summary>
        /// <param name="episodeInfo"></param>
        private void GetEpisodeInfo(XElement episodeInfo)
        {
            _synopsis = episodeInfo.Element("Overview").Value;
            //_runtime = int.Parse(episodeInfo.Element("Runtime").Value);
        }
        
        /// <summary>
        /// Get Misc Info specific to Queues
        /// </summary>
        private void GetMisc()
        {
            _runtime = Source.Element("runtime") == null ? 0 : int.Parse(Source.Element("runtime").Value) / 60;
        }

        /// <summary>
        /// Get the categories from the XML
        /// </summary>
        private void GetCategories()
        {
            //--------------------------------------------------------------------------------
            // Parse the categories and stuff them in their respective properties
            //--------------------------------------------------------------------------------
            foreach (XElement node in Source.Elements("category"))
            {
                if (node.Attribute("scheme").Value.Contains("queue_availability"))
                    _availability = node.Attribute("label").Value;
                else if (node.Attribute("scheme").Value.Contains("genres"))
                    _genres.Add(node.Attribute("label").Value);
                else if (node.Attribute("scheme").Value.Contains("mpaa_ratings"))
                    _ratings.Add(node.Attribute("label").Value);
                else if (node.Attribute("scheme").Value.Contains("tv_ratings"))
                    _ratings.Add(node.Attribute("label").Value);
            }
        }

        /// <summary>
        /// Get the Box Art URLs from the XML
        /// </summary>
        private void GetBoxArt()
        {
            //--------------------------------------------------------------------------------
            // Parse the boxart
            //--------------------------------------------------------------------------------
            foreach (XElement node in Source.Elements("box_art"))
            {
                _boxArt.Add(node.Attribute("small").Value);
                _boxArt.Add(node.Attribute("medium").Value);
                _boxArt.Add(node.Attribute("large").Value);

                //--------------------------------------------------------------------------------
                // Set the cover id for the episode or movie
                //--------------------------------------------------------------------------------
                if (_isEpisode)
                    _coverId = Source.Element("id").Value.Remove(0, Source.Element("id").Value.LastIndexOf('/') + 1);
                else
                    _coverId = node.Attribute("large").Value.Substring(node.Attribute("large").Value.LastIndexOf("/") + 1, node.Attribute("large").Value.LastIndexOf(".") - node.Attribute("large").Value.LastIndexOf("/") - 1);

                //--------------------------------------------------------------------------------
                // Get the backdrop id. This diffres from the cover id only if its an episode
                //--------------------------------------------------------------------------------
                _backdropId = node.Attribute("large").Value.Substring(node.Attribute("large").Value.LastIndexOf("/") + 1, node.Attribute("large").Value.LastIndexOf(".") - node.Attribute("large").Value.LastIndexOf("/") - 1);
            }
        }

        /// <summary>
        /// Get the Links from the XML
        /// </summary>
        private void GetLinks()
        {
            //--------------------------------------------------------------------------------
            // Parse all the links to expands
            //--------------------------------------------------------------------------------
            foreach (XElement node in Source.Elements("link"))
            {
                switch (node.Attribute("title").Value)
                {
                    case "synopsis":
                        _synopsisLink = node.Attribute("href").Value;
                        foreach (XElement childnode in node.Elements("synopsis"))
                        {
                            _synopsis = Regex.Replace(childnode.Value.ToString(), "<[^>]*>", string.Empty);
                        }
                        break;
                    case "formats":
                        //--------------------------------------------------------------------------------
                        // This code was too big to place here, so we expanded it below
                        //--------------------------------------------------------------------------------
                        _formatsLink = node.Attribute("href").Value;
                        GetFormats(node);
                        break;
                    case "screen formats":
                        break;
                    case "cast":
                        _castLink = node.Attribute("href").Value;
                        break;
                    case "languages and audio":
                        break;
                    case "discs":
                        _discLink = node.Attribute("href").Value;
                        _isSeries = true;
                        break;
                    case "episodes":
                        _episodesLink = node.Attribute("href").Value;
                        break;
                    case "seasons":
                        _seasonsLink = node.Attribute("href").Value;
                        _isSeries = true;
                        break;
                    case "similars":
                        _similarLink = node.Attribute("href").Value;
                        break;
                    case "web page":
                        break;
                    default:
                        if (node.Attribute("title").Value.Replace("\"", "") == Title)
                        {
                            _titleRef = node.Attribute("href").Value;

                            //--------------------------------------------------------------------------------
                            // Lets check to see if its a series or season
                            //--------------------------------------------------------------------------------
                            if (_titleRef.Contains("series") || _titleRef.Contains("season"))
                                _isSeries = true;
                        }
                        break;
                }

                if (node.Attribute("rel").Value.Contains("series"))
                    _isSeries = true;
            }
        }

        /// <summary>
        /// Get the formats from the XML
        /// </summary>
        private void GetFormats(XElement formatSource)
        {
            //--------------------------------------------------------------------------------
            // If no formats are included, lets get them from NetFlix
            //--------------------------------------------------------------------------------
            if (formatSource.Element("delivery_formats") == null)
                formatSource = Formats.GetDiscFormats(_formatsLink, _user);

            //--------------------------------------------------------------------------------
            // Lets parse the delivery formats
            //--------------------------------------------------------------------------------
            foreach (XElement child in formatSource.Descendants("availability"))
            {
                Formats fFormat = new Formats();

                //--------------------------------------------------------------------------------
                // Lets get the available dates
                //--------------------------------------------------------------------------------
                if (child.Attribute("available_from") != null)
                    fFormat.AvailableFrom = ClrExtensions.DateTimeExtension.FromUnixTime(long.Parse(child.Attribute("available_from").Value));

                if (child.Attribute("available_until") != null)
                    fFormat.AvailableUntil = ClrExtensions.DateTimeExtension.FromUnixTime(long.Parse(child.Attribute("available_until").Value));

                //--------------------------------------------------------------------------------
                // Now lets see what format it is
                //--------------------------------------------------------------------------------
                switch (child.Element("category").Attribute("label").Value)
                {
                    case "instant":
                        fFormat.Format = Formats.DiscFormat.Instant;
                        if (fFormat.AvailableFrom <= DateTime.Now && fFormat.AvailableUntil >= DateTime.Now)
                            _isInstant = true;
                        break;
                    case "DVD":
                        fFormat.Format = Formats.DiscFormat.DVD;
                        _isDvd = true;
                        break;
                    case "Blu-ray":
                        fFormat.Format = Formats.DiscFormat.BluRay;
                        _isBluRay = true;
                        break;
                }

                _titleFormats.Add(fFormat);
            }
        }

        /// <summary>
        /// Get the Background Image
        /// </summary>
        private void GetBackgroundImage()
        {
            int tries = 0;
            string title = Title;
            string thisImageURL = "";

            //--------------------------------------------------------------------------------
            // Lets try 3 times to get a good image.
            // Pass 1 is the title
            // Pass 2 removes the ':'
            // Pass 3 removes the special characters (quotes, etc...)
            //--------------------------------------------------------------------------------
            while (tries < 3)
            {
                if (tries == 1)
                {
                    if (title.Contains(":"))
                        title = title.Substring(0, title.IndexOf(":"));
                }
                if (tries == 2)
                    title = Regex.Replace(title, @"\d", "");

                try
                {
                    //--------------------------------------------------------------------------------
                    // Get the potential images from TheMovieDb.org
                    //--------------------------------------------------------------------------------
                    XDocument xdoc = XDocument.Load("http://api.themoviedb.org/2.1/Movie.search/en/xml/" + _theMovieDbKey + "/" + title + "+" + ReleaseYear);

                    //--------------------------------------------------------------------------------
                    // Lets check to see if we have any good images
                    //--------------------------------------------------------------------------------
                    if (xdoc.Element("OpenSearchDescription").Element("movies").Value != "Nothing found.")
                    {
                        IEnumerable<XElement> address = from el in xdoc.Element("OpenSearchDescription").Element("movies").Element("movie").Element("images").Elements("image")
                                                        where (string)el.Attribute("type") == "backdrop" && (string)el.Attribute("size") == "poster"
                                                        select el;

                        foreach (XElement el in address)
                        {
                            thisImageURL = el.Attribute("url").Value;
                            tries = 5;
                            break;
                        }
                    }
                }
                catch (Exception ex)
                {
                    Log.Error("My NetFlix - QueueItem:GetBackgroundImage: Error - " + ex.Message);
                    tries = 5;
                    break;
                }

                tries++;
            }

            //--------------------------------------------------------------------------------
            // Lets call the GetBanner method in the imageprocessor class to get the image.
            //--------------------------------------------------------------------------------
            if(thisImageURL != "")
                base.asyncGetBanner(thisImageURL, BackDropId);
        }

        /// <summary>
        /// Get background image for a TV Series
        /// </summary>
        private void GetSeriesBackgroundImage()
        {
            string bannerImage = "";
            string subTitle = "";

            try
            {
                subTitle = Title.Contains(":") ? Title.Substring(0, Title.IndexOf(':')) : Title;
                XDocument theTVDBCallSeries = XDocument.Load("http://thetvdb.com/api/GetSeries.php?seriesname=" + subTitle);

                //--------------------------------------------------------------------------------
                // Now lets get potential matches from thetvdb.com
                //--------------------------------------------------------------------------------
                foreach (XElement xe in theTVDBCallSeries.Descendants("Series"))
                {
                    if (xe.Element("SeriesName").Value == subTitle)
                    {
                        XDocument theTVdbSeriesInfo = XDocument.Load("http://thetvdb.com/api/" + _theTVDbKey + "/series/" + xe.Element("id").Value + "/");

                        bannerImage = "http://thetvdb.com/banners/" + theTVdbSeriesInfo.Element("Data").Element("Series").Element("fanart").Value;
                        _theTVDbId = xe.Element("id").Value;
                    }

                }
            }
            catch (Exception ex)
            {
                Log.Error("My NetFlix - QueueItem:QueueItem: Error - " + ex.Message);
            }

            //--------------------------------------------------------------------------------
            // If the file exists exit.
            //--------------------------------------------------------------------------------
            if (!File.Exists(Config.GetFolder(Config.Dir.Thumbs) + "\\MyNetFlix\\BackDrops\\" + _backdropId + ".jpg"))
            {

                //--------------------------------------------------------------------------------
                // if an image is found, lets get  it
                //--------------------------------------------------------------------------------
                if (bannerImage != "")
                {
                    base.asyncGetBanner(bannerImage, _backdropId);
                }
            }
        }

        /// <summary>
        /// Get the cover image for the item
        /// </summary>
        private void GetCoverImage()
        {
            //--------------------------------------------------------------------------------
            // Get the image from the GetCover method...
            //--------------------------------------------------------------------------------
             base.GetCover(_boxArt[2].ToString(), _coverId);
        }

        #endregion

    }
}
